home *** CD-ROM | disk | FTP | other *** search
/ Developer CD Series 2000 November: Tool Chest / Dev.CD Nov 00 TC Disk 1.toast / Sample Code / Networking / StreamNOP / StreamNOP.c < prev    next >
Encoding:
Text File  |  2000-10-06  |  14.3 KB  |  444 lines  |  [TEXT/CWIE]

  1. /*
  2.     File:        StreamNOP.c
  3.  
  4.     Contains:    Stream module that does nothing.
  5.  
  6.     Written by:    Quinn "The Eskimo!"
  7.  
  8.     Copyright:    Copyright © 1997-2000 by Apple Computer, Inc., All Rights Reserved.
  9.  
  10.     Disclaimer:    IMPORTANT:  This Apple software is supplied to you by Apple Computer, Inc.
  11.                 ("Apple") in consideration of your agreement to the following terms, and your
  12.                 use, installation, modification or redistribution of this Apple software
  13.                 constitutes acceptance of these terms.  If you do not agree with these terms,
  14.                 please do not use, install, modify or redistribute this Apple software.
  15.  
  16.                 In consideration of your agreement to abide by the following terms, and subject
  17.                 to these terms, Apple grants you a personal, non-exclusive license, under Apple’s
  18.                 copyrights in this original Apple software (the "Apple Software"), to use,
  19.                 reproduce, modify and redistribute the Apple Software, with or without
  20.                 modifications, in source and/or binary forms; provided that if you redistribute
  21.                 the Apple Software in its entirety and without modifications, you must retain
  22.                 this notice and the following text and disclaimers in all such redistributions of
  23.                 the Apple Software.  Neither the name, trademarks, service marks or logos of
  24.                 Apple Computer, Inc. may be used to endorse or promote products derived from the
  25.                 Apple Software without specific prior written permission from Apple.  Except as
  26.                 expressly stated in this notice, no other rights or licenses, express or implied,
  27.                 are granted by Apple herein, including but not limited to any patent rights that
  28.                 may be infringed by your derivative works or by other works in which the Apple
  29.                 Software may be incorporated.
  30.  
  31.                 The Apple Software is provided by Apple on an "AS IS" basis.  APPLE MAKES NO
  32.                 WARRANTIES, EXPRESS OR IMPLIED, INCLUDING WITHOUT LIMITATION THE IMPLIED
  33.                 WARRANTIES OF NON-INFRINGEMENT, MERCHANTABILITY AND FITNESS FOR A PARTICULAR
  34.                 PURPOSE, REGARDING THE APPLE SOFTWARE OR ITS USE AND OPERATION ALONE OR IN
  35.                 COMBINATION WITH YOUR PRODUCTS.
  36.  
  37.                 IN NO EVENT SHALL APPLE BE LIABLE FOR ANY SPECIAL, INDIRECT, INCIDENTAL OR
  38.                 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  39.                 GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  40.                 ARISING IN ANY WAY OUT OF THE USE, REPRODUCTION, MODIFICATION AND/OR DISTRIBUTION
  41.                 OF THE APPLE SOFTWARE, HOWEVER CAUSED AND WHETHER UNDER THEORY OF CONTRACT, TORT
  42.                 (INCLUDING NEGLIGENCE), STRICT LIABILITY OR OTHERWISE, EVEN IF APPLE HAS BEEN
  43.                 ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  44.  
  45.     Change History (most recent first):
  46. */
  47.  
  48. /////////////////////////////////////////////////////////////////////
  49. // The OT debugging macros in <OTDebug.h> require this variable to
  50. // be set.
  51.  
  52. #ifndef qDebug
  53. #define qDebug    1
  54. #endif
  55.  
  56. /////////////////////////////////////////////////////////////////////
  57. // Determine whether this is going to be an instrumented build or not.
  58.  
  59. #ifndef INSTRUMENTATION_ACTIVE
  60.     #define INSTRUMENTATION_ACTIVE 0
  61. #else
  62.     #define INSTRUMENTATION_ACTIVE 1
  63. #endif
  64.  
  65. /////////////////////////////////////////////////////////////////////
  66. // Pick up all the standard OT module stuff.
  67.  
  68. #include <OpenTransportKernel.h>
  69.  
  70. /////////////////////////////////////////////////////////////////////
  71. // Pick up Instrumentation SDK stuff.  We only do this if we're
  72. // actually instrumenting, so you don't even have to have the SDK
  73. // to compile the non-instumented version of the code.  If we're
  74. // not instrumenting, we compile a bunch of bogus macros that generally
  75. // compile to nothing.
  76.  
  77. #if INSTRUMENTATION_ACTIVE
  78.     #include <InstrumentationMacros.h>
  79. #else
  80.     #define TRACE_SETUP        long __junk
  81.     #define LOG_ENTRY(n)    if (0) { __junk ; }
  82.     #define LOG_EXIT        if (0) { __junk ; }
  83. #endif
  84.  
  85. /////////////////////////////////////////////////////////////////////
  86. // To get OTDebugStr you have to link with OpenTptMiscUtilsPPC.o, which 
  87. // is not part of Universal Interfaces.  So we just define our own version, 
  88. // layered on top of the lowercase debugstr.
  89.  
  90. extern pascal void OTDebugStr(const char* str)
  91. {
  92.     debugstr(str);
  93. }
  94.  
  95. /////////////////////////////////////////////////////////////////////
  96. // Some simple routines we use in our various assertions.
  97.  
  98. static Boolean IsReadQ(queue_t* q)
  99.     // Returns true if q is the read queue of a queue pair.
  100. {
  101.     return ( (q->q_flag & QREADR) != 0 );
  102. }
  103.  
  104. static Boolean IsWriteQ(queue_t* q)
  105.     // Returns true if q is the write queue of a queue pair.
  106. {
  107.     return ( (q->q_flag & QREADR) == 0 );
  108. }
  109.  
  110. /////////////////////////////////////////////////////////////////////
  111. // Per-Stream information
  112.  
  113. // This structure is used to hold the per-stream data for the module.
  114. // While module's can use normal global variables to store real globals,
  115. // they must maintain their own per-stream data structures.  I use
  116. // mi_open_comm to allocate this data structure when the stream is
  117. // opened.  mi_open_comm stores the address of this data structure in the
  118. // read and write queue's q_ptr field, so the rest of the code
  119. // can get to it by calling the GetPerStreamData function.
  120.  
  121. enum {
  122.     kStreamNOPPerStreamDataMagic = 'NOOP'
  123. };
  124.  
  125. struct PerStreamData
  126. {
  127.     OSType                 magic;                // kStreamNOPPerStreamDataMagic = 'NOOP' for debugging
  128.     // Your per-stream data structures go here.
  129. };
  130. typedef struct PerStreamData PerStreamData, *PerStreamDataPtr;
  131.  
  132. static PerStreamDataPtr GetPerStreamData(queue_t* readOrWriteQ)
  133.     // You can pass both the read or the write queue to this routine
  134.     // because mi_open_comm sets up both q_ptr's to point to the
  135.     // queue local data.
  136.     //
  137.     // Note that, in order to avoid the overhead of a function call,
  138.     // you would normally use inline code (or a macro)
  139.     // to get your per-stream data instead of using a separate function.
  140.     // However I think the separate function makes things clearer.
  141.     // I also acts as a central bottleneck for my debugging code.
  142.     //
  143.     // Environment: any standard STREAMS entry point
  144. {
  145.     PerStreamDataPtr streamData;
  146.     
  147.     streamData = (PerStreamDataPtr) readOrWriteQ->q_ptr;
  148.  
  149.     OTAssert("GetPerStreamData: what streamData", streamData != nil);
  150.     OTAssert("GetPerStreamData: Bad magic", streamData->magic == kStreamNOPPerStreamDataMagic);
  151.     
  152.     return (streamData);
  153. }
  154.  
  155. // mi_open_comm and mi_close_comm (and also mi_detach and mi_close_detached)
  156. // use this global to store the list of open streams to this module.
  157.  
  158. static char* gStreamList = nil;
  159.  
  160. /////////////////////////////////////////////////////////////////////
  161. // Open routine
  162.  
  163. static SInt32 StreamNOPOpen(queue_t* rdq, dev_t* dev, SInt32 flag, SInt32 sflag, cred_t* creds)
  164.     // This routine is called by STREAMS when a new stream is connected to
  165.     // our module.  The bulk of the work here is done by the Mentat helper
  166.     // routine mi_open_comm.
  167.     //
  168.     // Environment: standard STREAMS entry point
  169. {
  170.     TRACE_SETUP;
  171.     int err;
  172.     PerStreamDataPtr streamData;
  173.  
  174.     LOG_ENTRY( "StreamNOP:StreamNOPOpen" );
  175.     
  176.     OTAssert("StreamNOPOpen: Not the read queue", IsReadQ(rdq) );
  177.  
  178.     OTDebugBreak("StreamNOPOpen");
  179.     
  180.     err = noErr;
  181.     
  182.     // If we already have per-stream data for this stream, the stream is being reopened.
  183.     // In that case, we can just return.
  184.     // Note that we can't call GetPerStreamData because it checks that streamData is not nil.
  185.     
  186.     if ( rdq->q_ptr != nil ) {
  187.         goto done;
  188.     }
  189.  
  190.     // Make sure we're being opened properly -- because we're a module we
  191.     // require a "module" open.  Other possibilities are the value 0 (used
  192.     // to open a specific minor device number (ie stream) on a device driver),
  193.     // and CLONEOPEN (used to open a new stream to a device number where you
  194.     // don't care what device number you get -- the typical behaviour for
  195.     // networking (as opposed to serial) devices).
  196.     
  197.     if ( (err == noErr) && (sflag != MODOPEN) ) {
  198.         err = ENXIO;
  199.     }
  200.     
  201.     // Use the mi_open_comm routine to allocate our per-stream data.  Then
  202.     // zero out the entire per-stream data record and fill out the fields
  203.     // we're going to need.
  204.     
  205.     if (err == noErr) {
  206.         err = mi_open_comm(&gStreamList, sizeof(PerStreamData), rdq, dev, flag, sflag, creds);
  207.         if ( err == noErr ) {
  208.             // Note that we can't call GetPerStreamData because the magic is not set up yet.
  209.             streamData = (PerStreamDataPtr) rdq->q_ptr;
  210.             
  211.             OTMemzero(streamData, sizeof(PerStreamData));
  212.             
  213.             streamData->magic = kStreamNOPPerStreamDataMagic;
  214.         }
  215.     }
  216.  
  217. done:
  218.     LOG_EXIT;
  219.     return (err);
  220. }
  221.  
  222. /////////////////////////////////////////////////////////////////////
  223. // Close routine
  224.  
  225. static SInt32 StreamNOPClose(queue_t* rdq, SInt32 flags, cred_t* credP)
  226.     // This routine is called by STREAMS when a stream is being
  227.     // disconnected from our module (ie closed).  The bulk of the work
  228.     // is done by the magic Mentat helper routine mi_close_comm.
  229.     //
  230.     // Environment: standard STREAMS entry point
  231. {
  232.     TRACE_SETUP;
  233.     #pragma unused(flags)
  234.     #pragma unused(credP)
  235.  
  236.     LOG_ENTRY( "StreamNOP:StreamNOPClose" );
  237.  
  238.     OTAssert("StreamNOPClose: Not the read queue", IsReadQ(rdq) );
  239.  
  240.     (void) mi_close_comm(&gStreamList, rdq);
  241.  
  242.     LOG_EXIT;
  243.     return (0);
  244. }
  245.  
  246. /////////////////////////////////////////////////////////////////////
  247.  
  248. enum {
  249.     kNoPrimitive = -1
  250. };
  251.  
  252. static long GetPrimitive(mblk_t* mp)
  253.     // GetPrimitive gets the TPI/DLPI primitive out of a message block.
  254.     // It returns kNoPrimitive if the message block is of the wrong
  255.     // type or there is no primitive.
  256.     //
  257.     // Environment: any standard STREAMS entry point
  258. {
  259.     if ((mp->b_datap->db_type == M_PROTO || mp->b_datap->db_type == M_PCPROTO) && MBLK_SIZE(mp) >= sizeof(long) ) {
  260.         return ( ( (union T_primitives*) mp->b_rptr)->type );
  261.     } else {
  262.         return ( kNoPrimitive );
  263.     }
  264. }
  265.  
  266. /////////////////////////////////////////////////////////////////////
  267. // Write-side put routine
  268.  
  269. static SInt32 StreamNOPWritePut(queue_t* q, mblk_t* mp)
  270.     // This routine is called by STREAMS when it has a message for our
  271.     // module from upstream.  Typically, this routine is a big case statement
  272.     // that dispatches to our various message handling routines.  However, the 
  273.     // function of this stream module is to pass through all messages unchanged,
  274.     // so the case statement is not very exciting.
  275.     //
  276.     // Environment: standard STREAMS entry point
  277. {
  278.     TRACE_SETUP;
  279.     PerStreamDataPtr streamData;
  280.     
  281.     LOG_ENTRY( "StreamNOP:StreamNOPWritePut" );
  282.  
  283.     OTAssert("StreamNOPWritePut: Not the write queue", IsWriteQ(q) );
  284.  
  285.     // OTDebugBreak("StreamNOPWritePut: Entered");
  286.     
  287.     streamData = GetPerStreamData(q);
  288.  
  289.     switch ( GetPrimitive(mp) ) {
  290.         default:
  291.             putnext(q, mp);
  292.             break;
  293.     }
  294.     
  295.     LOG_EXIT;
  296.     
  297.     return 0;
  298. }
  299.  
  300. /////////////////////////////////////////////////////////////////////
  301. // Read-side put routine
  302.  
  303. static SInt32 StreamNOPReadPut(queue_t* q, mblk_t* mp)
  304.     // This routine is called by STREAMS when it has a message for our
  305.     // module from downstream.  Typically, this routine is a big case statement
  306.     // that dispatches to our various message handling routines.  However, the 
  307.     // function of this stream module is to pass through all messages unchanged,
  308.     // so the case statement is not very exciting.
  309.     //
  310.     // Environment: standard STREAMS entry point
  311. {
  312.     TRACE_SETUP;
  313.     PerStreamDataPtr streamData;
  314.     
  315.     LOG_ENTRY( "StreamNOP:StreamNOPReadPut" );
  316.  
  317.     OTAssert("StreamNOPReadPut: Not the read queue", IsReadQ(q) );
  318.  
  319.     // OTDebugBreak("StreamNOPReadPut: Entered");
  320.     
  321.     streamData = GetPerStreamData(q);
  322.  
  323.     switch ( GetPrimitive(mp) ) {
  324.         default:
  325.             putnext(q, mp);
  326.             break;
  327.     }
  328.     
  329.     LOG_EXIT;
  330.     
  331.     return 0;
  332. }
  333.  
  334. /////////////////////////////////////////////////////////////////////
  335. // Static Declaration Structures
  336.  
  337. static struct module_info gModuleInfo =  
  338. {
  339.     9992,                        // Module Number, only useful for debugging
  340.     "StreamNOP",                // Name of module
  341.     0,                            // Minimum data size
  342.     INFPSZ,                        // Maximum data size
  343.     16384,                        // Hi water mark for queue
  344.     4096                        // Lo water mark for queue
  345. };
  346.  
  347. static struct qinit gReadInit = 
  348. {
  349.     StreamNOPReadPut,        // Put routine for "incoming" data
  350.     nil,                        // Service routine for "incoming" data
  351.     StreamNOPOpen,            // Our open routine
  352.     StreamNOPClose,         // Our close routine
  353.     nil,                        // No admin routine
  354.     &gModuleInfo                // Our module_info
  355. };
  356.  
  357. static struct qinit gWriteInit =
  358. {
  359.     StreamNOPWritePut,        // Put routine for client data
  360.     nil,                        // Service routine for client data
  361.     nil,                        // open  field only used in read-side structure
  362.     nil,                        // close field only used in read-side structure
  363.     nil,                        // admin field only used in read-side structure
  364.     &gModuleInfo                // Our module_info
  365. };
  366.  
  367. static struct streamtab theStreamTab = 
  368. {
  369.     &gReadInit,                    // Our read-side qinit structure
  370.     &gWriteInit,                // Our write-side qinit structure
  371.     0,                            // We are not a mux, so set this to nil
  372.     0                            // We are not a mux, so set this to nil
  373. };
  374.  
  375. /////////////////////////////////////////////////////////////////////
  376. // Macintosh-specific Static Structures
  377.  
  378. static struct install_info theInstallInfo =
  379. {
  380.     &theStreamTab,            // Stream Tab pointer
  381.     kOTModIsModule + kOTModUpperIsTPI + kOTModIsFilter,
  382.                             // Tell OT that we are a driver, not a module
  383.     SQLVL_MODULE,            // Synchronization level, module level for the moment
  384.     0,                        // Shared writer list buddy
  385.     0,                        // Open Transport use - always set to 0
  386.     0                        // Flag - always set to 0
  387. };
  388.  
  389. // Prototypes for the exported routines below.
  390.  
  391. extern Boolean InitStreamModule(void *portInfo);
  392. extern void TerminateStreamModule(void);
  393. extern install_info* GetOTInstallInfo();
  394.  
  395. #pragma export list GetOTInstallInfo, InitStreamModule, TerminateStreamModule
  396.  
  397. // Export entry point
  398.  
  399. extern Boolean InitStreamModule(void *portInfo)
  400.     // Initialises the module before the first stream is opened.
  401.     // Should return true if the module has started up correctly.
  402.     //
  403.     // Environment: Always called at SystemTask time.
  404. {    
  405.     TRACE_SETUP;
  406.     #pragma unused(portInfo)
  407.     Boolean result;
  408.     
  409.     OTDebugBreak("StreamNOP: InitStreamModule");
  410.     
  411.     LOG_ENTRY( "StreamNOP:InitStreamModule" );
  412.  
  413.     result = true;
  414.     
  415.     LOG_EXIT;
  416.     return (result);
  417. }
  418.  
  419. extern void TerminateStreamModule(void)
  420.     // Shuts down the module after the last stream has been
  421.     // closed.
  422.     //
  423.     // Environment: Always called at SystemTask time.
  424. {
  425.     TRACE_SETUP;
  426.     
  427.     LOG_ENTRY( "StreamNOP:TerminateStreamModule" );
  428.     
  429.     // It's an excellent idea to have the following in your code, just to make
  430.     // sure you haven't left any streams open before you quit.  In theory, OT
  431.     // should not call you until the last stream has been closed, but in practice
  432.     // this can happen if you use mi_detach to half-close a stream.
  433.     
  434.     OTAssert("TerminateStreamModule: Streams are still active", gStreamList == nil);
  435.  
  436.     LOG_EXIT;
  437. }
  438.  
  439. extern install_info* GetOTInstallInfo()
  440.     // Return pointer to install_info to STREAMS.
  441. {
  442.     return &theInstallInfo;
  443. }
  444.